﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Gola.VN.Scb.Security
{
    /// <summary>
    /// This class will encrypt and decrypt information based on the RC2 algorithm.
    /// </summary>
    [Serializable]
    public class Crypto
    {
        #region Private Variables

        //RC2 requires that the encryption key be 16 characters or less.  If you 
        //exceed 16 characters, the encryption and decryption routines will fail.
        private const string ENCRYPTIONKEY = "$E^%*yhujm,-ph14";

        //IV Key can be any length
        private const string IVKEY = @"-023i[rqkwaf.m;klf+|\/QK32";

        #endregion

        #region Static Methods

        public static string Encrypt(string stringToEncrypt)
        {
            return Encrypt(stringToEncrypt, ENCRYPTIONKEY, IVKEY);
        }

        private static string Encrypt(string stringToEncrypt, string encryptionKey, string ivKey)
        {
            var rc2CSP = new RC2CryptoServiceProvider();

            byte[] encrypted;

            //Validate the Encryption and IV keys and if valid, encrypt the data.
            if (encryptionKey == string.Empty)
                throw new Exception("Encryption Key Is Missing");

            if (encryptionKey.Length > 16)
                throw new Exception("Encryption Key Is Not Valid");

            if (ivKey == string.Empty)
                throw new Exception("IV Key Is Missing");

            try
            {
                //Get an encryptor.
                ICryptoTransform encryptor = rc2CSP.CreateEncryptor(Encoding.ASCII.GetBytes(encryptionKey),
                                                                    Encoding.ASCII.GetBytes(ivKey));

                //Encrypt the data.
                var msEncrypt = new MemoryStream();
                var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

                //Convert the data to a byte array.
                byte[] toEncrypt = Encoding.ASCII.GetBytes(stringToEncrypt);

                //Write all data to the crypto stream and flush it.
                csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
                csEncrypt.FlushFinalBlock();

                //Get encrypted array of bytes.
                encrypted = msEncrypt.ToArray();

                return Convert.ToBase64String(encrypted);
            }
            catch (CryptographicException ex)
            {
                throw new Exception(ex.Message, ex);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public static string Decrypt(string stringToDecrypt)
        {
            return Decrypt(stringToDecrypt, ENCRYPTIONKEY, IVKEY);
        }

        public static string Decrypt(string stringToDecrypt, string encryptionKey)
        {
            return Decrypt(stringToDecrypt, encryptionKey, IVKEY);
        }

        public static string Decrypt(string stringToDecrypt, string encryptionKey, string ivKey)
        {
            var rc2CSP = new RC2CryptoServiceProvider();

            byte[] encrypted;
            byte[] fromEncrypt;

            //Validate the Encryption and IV keys and if valid, decrypt the data.
            if (encryptionKey == string.Empty)
                throw new Exception("Encryption Key Is Missing");

            if (encryptionKey.Length > 16)
                throw new Exception("Encryption Key Is Not Valid");

            if (ivKey == string.Empty)
                throw new Exception("IV Key Is Missing");

            try
            {
                //Get a decryptor.
                ICryptoTransform decryptor = rc2CSP.CreateDecryptor(Encoding.ASCII.GetBytes(encryptionKey),
                                                                    Encoding.ASCII.GetBytes(ivKey));

                encrypted = Convert.FromBase64String(stringToDecrypt);

                var msDecrypt = new MemoryStream(encrypted);
                var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

                fromEncrypt = new byte[encrypted.Length];

                //Read the data out of the crypto stream.
                csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);

                //Convert the byte array back into a string.
                string val = Encoding.UTF8.GetString(fromEncrypt);
                val = val.Replace('\0'.ToString(), "");

                return val;
            }
            catch (CryptographicException ex)
            {
                throw new Exception("Unable To Decrypt.  Make Sure Encryption Key and IV Key are Valid", ex);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public static string MD5Hash(string source)
        {
            MD5 hasher = new MD5CryptoServiceProvider();
            var ascEncoding = new ASCIIEncoding();
            return BitConverter.ToString(hasher.ComputeHash(ascEncoding.GetBytes(source))).Replace("-", "").ToLower();
        }

        public static string SHA512Hash(string LoginID, string source)
        {
            SHA512 hasher = new SHA512Managed();
            var ascEncoding = new ASCIIEncoding();
            return
                BitConverter.ToString(hasher.ComputeHash(ascEncoding.GetBytes(LoginID.ToUpper() + source))).Replace(
                    "-", "").ToLower();
        }

        public static bool ComparePasswords(string unencryptedPassword, string userID, string encryptedPassword)
        {
            if (unencryptedPassword == null)
            {
                throw new ArgumentNullException("unencryptedPassword");
            }
            if (userID == null)
            {
                throw new ArgumentNullException("userID");
            }
            if (encryptedPassword == null)
            {
                throw new ArgumentNullException("encryptedPassword");
            }
            if (unencryptedPassword.Length == 0)
            {
                throw new ArgumentOutOfRangeException("unencryptedPassword", "string is empty");
            }
            if (userID.Length == 0)
            {
                throw new ArgumentOutOfRangeException("userID", "string is empty");
            }
            if (encryptedPassword.Length == 0)
            {
                throw new ArgumentOutOfRangeException("encryptedPassword", "string is empty");
            }

            string hashedSHA = SHA512Hash(userID, unencryptedPassword);
            string hashedMD5 = MD5Hash(unencryptedPassword);
            return (string.Equals(hashedSHA, encryptedPassword) || string.Equals(hashedMD5, encryptedPassword));
        }

        // Hash an input string and return the hash as
        // a 32 character hexadecimal string.
        public static string GetMd5Hash(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        // Verify a hash against a string.
        public static bool VerifyMd5Hash(string input, string hash)
        {
            // Hash the input.
            string hashOfInput = GetMd5Hash(input);

            // Create a StringComparer an compare the hashes.
            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, hash))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion
    }
}